package com.spic.system.activiti.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.spic.common.core.domain.R;
import com.spic.common.core.utils.SecurityUtils;
import com.spic.common.core.utils.StringUtils;
import com.spic.common.core.web.domain.AjaxResult;
import com.spic.common.core.web.page.PageDomain;
import com.spic.common.core.web.page.TableSupport;
import com.spic.common.security.utils.cache.UserCacheUtils;
import com.spic.job.api.RemoteActivitiService;
import com.spic.job.api.domain.HiddenGovernance;
import com.spic.system.activiti.domain.BusinessStatus;
import com.spic.system.activiti.domain.DelelteExecutionCommand;
import com.spic.system.activiti.domain.DeleteTaskCommand;
import com.spic.system.activiti.domain.dto.ActWorkflowFormDataDTO;
import com.spic.system.activiti.domain.dto.TaskTodoDTO;
import com.spic.system.activiti.enums.BusinessStatusEnum;
import com.spic.system.activiti.service.IActTaskService;
import com.spic.system.activiti.service.IBusinessStatusService;
import com.spic.system.activiti.service.IMyTaskService;
import com.spic.system.api.RemoteBusinessService;
import com.spic.system.api.domain.ActTaskStartDto;
import com.spic.system.api.domain.HiddenTodoTask;
import com.spic.system.api.domain.TodoTaskDto;
import com.spic.system.domain.vo.DoTaskParam;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.ParallelGateway;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestParam;

import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 工作流服务处理
 * @Author AnQi
 * @Date 2021/11/18
 */
@Service
@Transactional
public class MyTaskServiceImpl implements IMyTaskService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private IActTaskService actTaskService;

    @Autowired
    private IBusinessStatusService businessStatusService;

    @Autowired
    private RemoteActivitiService remoteActivitiService;
    @Autowired
    private RemoteBusinessService remoteBusinessService;

    /**
     * 被其他服务调用，分布式事务被调用方加“Propagation.REQUIRES_NEW”属性
     * @param dto
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Object   startInstance(ActTaskStartDto dto){
        Long userId = SecurityUtils.getUserId();
        //设置发起人
        Authentication.setAuthenticatedUserId(String.valueOf(userId));
        //发起流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(dto.getKey(),dto.getBusiness(),dto.getVars());
        String instanceId = processInstance.getProcessInstanceId();
        //设置流程名称。
        runtimeService.setProcessInstanceName(instanceId, dto.getInstanceName());
        runtimeService.setVariable(instanceId,"businessType", dto.getBusinessType());
        businessStatusService.add(dto.getBusiness(),dto.getBusinessType(),instanceId);  //插入业务数据
        //自动流转
        if(dto.getVars().size()>1){
            automaticTransfer(instanceId, dto.getVars());
        }
        return  instanceId;
    }

    /**
     *
     * @param instanceId
     * @param nextMap  下个节点的审批人map
     */
    public void automaticTransfer(String instanceId,Map<String,Object> nextMap)
    {
        Task task = null;
        TaskQuery query = taskService.createTaskQuery().taskAssignee(SecurityUtils.getUserId()+"").active();
        List<Task> todoList = query.list();//获取申请人的待办任务列表
        for (Task tmp : todoList) {
            if(tmp.getProcessInstanceId().equals(instanceId)){
                task = tmp;//获取当前流程实例，当前申请人的待办任务
                break;
            }
        }
        taskService.complete(task.getId(),nextMap);
        //更新流程状态
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        if(CollectionUtils.isEmpty(taskList)){
            businessStatusService.updateStateByInstanceId(task.getProcessInstanceId(), BusinessStatusEnum.FINISH);
        }else{
            //更新为处理中
            BusinessStatus businessStatus = new BusinessStatus();
            businessStatus.setProcessInstanceId(task.getProcessInstanceId());
            List<BusinessStatus> businessStatusesList = businessStatusService.selectBusinessStatusList(businessStatus);
            if(businessStatusesList!=null && businessStatusesList.size()>0){
                BusinessStatus businessStatusnew = businessStatusesList.get(0);
                if(businessStatusnew.getStatus()!=BusinessStatusEnum.PROCESS.getCode()){
                    businessStatusService.updateStateByInstanceId(task.getProcessInstanceId(), BusinessStatusEnum.PROCESS);
                }
            }
        }
    }

    /**
     * 完成任务
     * @param taskId 任务ID
     */
    public void completeTask(String taskId) {
        String userId = String.valueOf(SecurityUtils.getUserId());
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(userId).singleResult();
        if (null != task) {
            taskService.complete(taskId);
        }
    }

    public void completeWithComment(String taskId, DoTaskParam doTaskParams) {
        Map<String, Object> params = doTaskParams.getParams();
        String assigneeObject = params.get("assigneeObject")==null?"":params.get("assigneeObject")+"";  //获取form 审批意见表单值
        String taskMap = params.get("taskMap")==null?"":params.get("taskMap")+"";  //获取 下一步候选人map
        JSONObject formJson = JSONObject.parseObject(assigneeObject);
        Map<String,Object> varMap = new HashMap<>();

        if(StringUtils.isNotEmpty(taskMap)){
            varMap = (Map)JSONObject.parse(taskMap);
        }
        String comment="";
        if(formJson!=null){
            comment = formJson.get("comment")==null?"":formJson.get("comment")+"";  //获取意见
        }
        String userId0 = String.valueOf(SecurityUtils.getUserId());
        Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(userId0).singleResult();
        taskService.setVariables(taskId,varMap); //设置全局变量
        //sendDuplicateTask(task, doTaskParams.getRecipients());
        if (null != task) {
            if (StringUtils.isNotEmpty(comment)) {
                taskService.addComment(taskId, task.getProcessInstanceId(), comment);
            }

            // 完成任务  并指定下个节点审批人 varMap
            taskService.complete(taskId,varMap);
        }
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();
        if(CollectionUtils.isEmpty(taskList)){
            businessStatusService.updateStateByInstanceId(task.getProcessInstanceId(), BusinessStatusEnum.FINISH);
            HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
            HistoricProcessInstance historicProcessInstance = query.processInstanceId(task.getProcessInstanceId()).singleResult();
            String name = historicProcessInstance.getName();
            String assignee = task.getAssignee();    //流程发起人

            Long userId=Long.valueOf(assignee);
            remoteBusinessService.sendNotic(userId,name) ;
        }else{
            //更新为处理中
            BusinessStatus businessStatus = new BusinessStatus();
            businessStatus.setProcessInstanceId(task.getProcessInstanceId());
            List<BusinessStatus> businessStatusesList = businessStatusService.selectBusinessStatusList(businessStatus);
            if(businessStatusesList!=null && businessStatusesList.size()>0){
                BusinessStatus businessStatusnew = businessStatusesList.get(0);
                if(businessStatusnew.getStatus()!=BusinessStatusEnum.PROCESS.getCode()){
                    businessStatusService.updateStateByInstanceId(task.getProcessInstanceId(), BusinessStatusEnum.PROCESS);
                }
            }
        }
    }
    // 抄送任务 TODO 存在问题
    private void sendDuplicateTask(Task task, List<String> recipients) {
        if(Objects.isNull(recipients)) return;
        if(CollectionUtils.isEmpty(recipients)) return;
        for(String recipient : recipients){
            Task newtask = taskService.newTask();
            newtask.setName(task.getName());
            newtask.setAssignee(recipient);
            newtask.setParentTaskId(task.getId());
            taskService.saveTask(newtask);
        }
    }

    public AjaxResult backProcess(@RequestParam String taskId,
                                  @RequestParam String targetActivityId) {
        try {
            // 1. 查询当前任务信息
            Task task = taskService.createTaskQuery()
                    .taskId(taskId)
                    .taskAssignee(SecurityUtils.getUserId()+"")
                    .singleResult();
            if(task == null) {
                return AjaxResult.error("当前任务不存在或你不是任务办理人");
            }

            String procInstId = task.getProcessInstanceId();

            // 2. 获取流程模型实例 BpmnModel
            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
            // 3. 当前节点信息
            FlowNode curFlowNode = (FlowNode)bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
            // 4. 获取当前节点的原出口连线
            List<SequenceFlow> sequenceFlowList = curFlowNode.getOutgoingFlows();
            // 5. 临时存储当前节点的原出口连线
            List<SequenceFlow> oriSequenceFlows = new ArrayList<>();
            oriSequenceFlows.addAll(sequenceFlowList);
            // 6. 将当前节点的原出口清空
            sequenceFlowList.clear();

            // 7. 获取目标节点信息
            FlowNode targetFlowNode = (FlowNode)bpmnModel.getFlowElement(targetActivityId);
            // 8. 获取驳回的新节点
            // 获取目标节点的入口连线
            List<SequenceFlow> incomingFlows = targetFlowNode.getIncomingFlows();
            // 存储所有目标出口
            List<SequenceFlow> allSequenceFlow = new ArrayList<>();
            for (SequenceFlow incomingFlow : incomingFlows) {
                // 找到入口连线的源头（获取目标节点的父节点）
                FlowNode source = (FlowNode)incomingFlow.getSourceFlowElement();
                List<SequenceFlow> sequenceFlows;
                if(source instanceof ParallelGateway) {
                    // 并行网关: 获取目标节点的父节点（并行网关）的所有出口，
                    sequenceFlows = source.getOutgoingFlows();
                } else {
                    // 其他类型父节点, 则获取目标节点的入口连续
                    sequenceFlows = targetFlowNode.getIncomingFlows();
                }
                allSequenceFlow.addAll(sequenceFlows);
            }

            // 9. 将当前节点的出口设置为新节点
            curFlowNode.setOutgoingFlows(allSequenceFlow);

            // 10. 完成当前任务，流程就会流向目标节点创建新目标任务
            //      删除已完成任务，删除已完成并行任务的执行数据 act_ru_execution
            List<Task> list = taskService.createTaskQuery().processInstanceId(procInstId).list();
            for (Task t : list) {
                if(taskId.equals(t.getId())) {
                    // 当前任务，完成当前任务
                    String message = String.format("【%s 驳回任务 %s => %s】",
                            SecurityUtils.getUsername(), task.getName(), targetFlowNode.getName());
                    taskService.addComment(t.getId(), procInstId, message);
                    // 完成任务，就会进行驳回到目标节点，产生目标节点的任务数据
                    taskService.complete(taskId);
                    // 删除执行表中 is_active_ = 0的执行数据， 使用command自定义模型
                    DelelteExecutionCommand deleteExecutionCMD = new DelelteExecutionCommand(task.getExecutionId());
                    managementService.executeCommand(deleteExecutionCMD);
                }else {
                    // 删除其他未完成的并行任务
                    // taskService.deleteTask(taskId); // 注意这种方式删除不掉，会报错：流程正在运行中无法删除。
                    // 使用command自定义命令模型来删除，直接操作底层的删除表对应的方法，对应的自定义是否删除
                    DeleteTaskCommand deleteTaskCMD = new DeleteTaskCommand(t.getId());
                    managementService.executeCommand(deleteTaskCMD);
                }
            }

            // 13. 完成驳回功能后，将当前节点的原出口方向进行恢复
            curFlowNode.setOutgoingFlows(oriSequenceFlows);


            // 12. 查询目标任务节点历史办理人
            List<Task> newTaskList = taskService.createTaskQuery().processInstanceId(procInstId).list();
            for (Task newTask : newTaskList) {
                // 取之前的历史办理人
                HistoricTaskInstance oldTargerTask = historyService.createHistoricTaskInstanceQuery()
                        .taskDefinitionKey(newTask.getTaskDefinitionKey()) // 节点id
                        .processInstanceId(procInstId)
                        .finished() // 已经完成才是历史
                        .orderByTaskCreateTime().desc() // 最新办理的在最前面
                        .list().get(0);
                taskService.setAssignee(newTask.getId(), oldTargerTask.getAssignee());
            }

            return AjaxResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("驳回失败："+ e.getMessage());
        }
    }

    @Override
    public List<HiddenTodoTask> getHiddenTodos(HiddenTodoTask hiddenTodoTask)
    {
        String taskKey = hiddenTodoTask.getTaskKey();
        List<HiddenTodoTask> tasks = Lists.newArrayList();
        String userId = String.valueOf(SecurityUtils.getUserId());
        TaskQuery desc = taskService.createTaskQuery().includeProcessVariables().taskAssignee(userId).orderByTaskCreateTime().desc();
        List<Task> todos = desc.list();
        if (todos == null || todos.size() == 0) {
            return tasks;
        }
        Set<String> instanceIds = todos.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet());
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processInstanceIds(instanceIds).list();
        Map<String, ProcessInstance> instanceMap = list.stream().collect(Collectors.toMap(ProcessInstance::getProcessInstanceId, instance -> instance,(k1, k2) -> k2));
        for(Task task : todos){
            HiddenTodoTask taskDto = new HiddenTodoTask();
            Map<String, Object> vars = task.getProcessVariables();
            String businessType = (String) vars.get("businessType");

            if(StringUtils.isEmpty(businessType) || !businessType.equals("HIDDEN_GOVERNANCE") || !taskKey.equals(task.getTaskDefinitionKey())){
                continue;
            }
            String instanceId = task.getProcessInstanceId();
            taskDto.setInstanceName(instanceMap.get(instanceId) == null ? "" : instanceMap.get(instanceId).getName());
            String creator = instanceMap.get(instanceId) == null ? "" : instanceMap.get(instanceId).getStartUserId();
            try {
                taskDto.setCreator(creator);
                if(!"".equals(creator) && !"null".equals(creator)){
                    taskDto.setCreatorName(UserCacheUtils.getValue(Long.valueOf(creator)));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            R<HiddenGovernance> result = remoteActivitiService.getInfo(instanceMap.get(instanceId) == null ? null : Integer.valueOf(instanceMap.get(instanceId).getBusinessKey()));

            if(result.getData()!=null){
                BeanUtils.copyProperties(result.getData(),taskDto);
            }else{
                continue;
            }
            BusinessStatus businessStatus = new BusinessStatus();
            businessStatus.setProcessInstanceId(task.getProcessInstanceId());
            List<BusinessStatus> businessStatusesList = businessStatusService.selectBusinessStatusList(businessStatus);
            if(businessStatusesList!=null && businessStatusesList.size()>0){
                BusinessStatus businessStatusnew = businessStatusesList.get(0);
                Integer status = businessStatusnew.getStatus();
                taskDto.setStatus(status);
            }
            taskDto.setStartTime(instanceMap.get(instanceId) == null ? null : instanceMap.get(instanceId).getStartTime());
            taskDto.setBusinessKey(instanceMap.get(instanceId) == null ? null : instanceMap.get(instanceId).getBusinessKey());
            taskDto.setAssignee(task.getAssignee());
            taskDto.setId(task.getId());
            taskDto.setBusinessType(businessType);
            taskDto.setTaskName(task.getName());
            taskDto.setInstanceId(task.getProcessInstanceId());
            taskDto.setFormKey(task.getFormKey());
            taskDto.setTaskKey(task.getTaskDefinitionKey());
            tasks.add(taskDto);
        }
        return tasks;
    }

    @Override
    public AjaxResult getTodos(TaskTodoDTO taskTodoDTO)
    {
        PageDomain pageDomain = TableSupport.getPageDomain();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        Integer begin = (pageNum -1) *  pageSize;
        String userId = String.valueOf(SecurityUtils.getUserId());
        TaskQuery desc = taskService.createTaskQuery().includeProcessVariables().taskAssignee(userId).orderByTaskCreateTime().desc();

        if(StringUtils.isNotEmpty(taskTodoDTO.getTaskName())){
            desc.taskNameLike("%" + taskTodoDTO.getTaskName() + "%");
        }
        List<TodoTaskDto> tasks = Lists.newArrayList();
        List<Task> todos = desc.listPage(begin, pageSize);
        Long total = desc.count();
        if (todos == null || todos.size() == 0) {
            Map<String, Object> res = new HashMap<>();
            res.put("total", total);
            res.put("list", tasks);
            return AjaxResult.success();
        }
        Set<String> instanceIds = todos.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet());
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processInstanceIds(instanceIds).list();
        Map<String, ProcessInstance> instanceMap = list.stream().collect(Collectors.toMap(ProcessInstance::getProcessInstanceId, instance -> instance,(k1, k2) -> k2));

        for (Task todo : todos)
        {

            String instanceId = todo.getProcessInstanceId();
            String businessKey = instanceMap.get(instanceId) == null ? null : instanceMap.get(instanceId).getBusinessKey();
            if(StringUtils.isEmpty(businessKey)){
                continue;
            }
            TodoTaskDto task = new TodoTaskDto();
            Map<String, Object> vars = todo.getProcessVariables();
            String businessType = (String) vars.get("businessType");
            task.setInstanceName(instanceMap.get(instanceId) == null ? "" : instanceMap.get(instanceId).getName());
            String creator = instanceMap.get(instanceId) == null ? "" : instanceMap.get(instanceId).getStartUserId();
            try {
                task.setCreator(creator);
                if(!"".equals(creator) && !"null".equals(creator)){
                    task.setCreatorName(UserCacheUtils.getValue(Long.valueOf(creator)));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            task.setStartTime(instanceMap.get(instanceId) == null ? null : instanceMap.get(instanceId).getStartTime());
            task.setBusinessKey(businessKey);
            task.setAssignee(todo.getAssignee());
            task.setId(todo.getId());
            task.setBusinessType(businessType);
            task.setTaskName(todo.getName());
            task.setInstanceId(todo.getProcessInstanceId());
            task.setFormKey(todo.getFormKey());
            task.setTaskKey(todo.getTaskDefinitionKey());
            tasks.add(task);
        }
        Map<String, Object> res = new HashMap<>();
        res.put("total", total);
        res.put("list", tasks);
        return AjaxResult.success(res);
    }

    /**
     * 根据流程实例id获取流程任务当前节点信息列表
     * @return todos 任务当前节点key列表
     */
    @Override
    public List<String> getTaskInfoList(List<String> list) {
        List<String> keyList = new ArrayList<>();
        for(String processInstanceId : list){
            //根据流程实例 ID 获取当前活动任务
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).active().singleResult();
            String key = task.getTaskDefinitionKey();
            keyList.add(key);
        }
        return keyList;
    }

    /**
     * 会签流程   根据instanceId 查询 task 任务列表
     * @param processInstanceId
     * @return
     */
    @Override
    public List<String> getMulitInstanceTask(String processInstanceId,String definitonKey)
    {
        List<String> list = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).taskDefinitionKey(definitonKey).active().list();
        for (Task task : taskList)
        {
            list.add(task.getAssignee());
        }
        return list;
    }

    @Override
    public List<String> getRunTaskInstanceTask(String processInstanceId)
    {
        List<String> list = new ArrayList<>();
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).active().list();
        if(taskList!=null && taskList.size()>0){
            list.add("success");
        }
        return list;
    }

    public int formDataSave(String taskID, List<ActWorkflowFormDataDTO> formData ) throws ParseException {
        return actTaskService.formDataSave(taskID, formData);
    }
}
